using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using AngryPig.Data;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using FarseerPhysics;
using FarseerPhysics.Dynamics.Contacts;

namespace AngryPig
{
    public class AngryPigGame : Microsoft.Xna.Framework.Game
    {
        const float BirdSize = 30;
        const float PigSize = 30;
        const float PhysicsToView = 50;
        //const float ViewToPhysics = 1.0f / 50;
        const float ViewToPhysics = 0.02f;

        static readonly Vector2 PivitPoint = new Vector2(70, 385);

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D background;
        Texture2D pigTexture;
        Texture2D brickTexture;
        Texture2D birdTexture;
        Texture2D brickPiece1;
        Texture2D brickPiece2;
        LevelInfo level;

        World world;
        const int MAX_SIZE = 20;
        Body[] pieceArray = new Body[MAX_SIZE]; 
        
        Body pig;
        List<Body> bricks = new List<Body>();
        List<Body> birds = new List<Body>();
        List<Body> brickPiece = new List<Body>();

        MouseState mouseStateLastFrame;

        public AngryPigGame()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 800;
            graphics.PreferredBackBufferHeight = 480;
            IsMouseVisible = true;
            Content.RootDirectory = "Content";
            
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            background = Content.Load<Texture2D>(@"Textures/Background");
            pigTexture = Content.Load<Texture2D>(@"Textures/Element133");
            brickTexture = Content.Load<Texture2D>(@"Textures/Element156");
            birdTexture = Content.Load<Texture2D>(@"Textures/Element130");
            brickPiece1 = Content.Load<Texture2D>(@"Textures/Element52");
            brickPiece2 = Content.Load<Texture2D>(@"Textures/Element53");
            level = Content.Load<LevelInfo>(@"Levels/Level01");

            Settings.PositionIterations = 100;

            world = new World(Vector2.UnitY * 9.8f);
            world.ContactManager.PostSolve = PostSolveDelegate;
            for (int i = 0; i < MAX_SIZE; i++)
            {
                pieceArray[i] = BodyFactory.CreateRectangle(world, brickPiece1.Width / 2 * ViewToPhysics, brickPiece1.Height / 2 * ViewToPhysics, 1);
            }
            for (int i = 0; i < 10; i++)
            {
                for (int j = i + 1; j < 10; j++)
                {
                    pieceArray[i].IgnoreCollisionWith(pieceArray[j]);
                }
            }
            //create bodies for brick
            foreach (var brick in level.Bricks)
            {
                //var body = BodyFactory.CreateCapsule(world, brick.Size.X * ViewToPhysics, brick.Size.Y * ViewToPhysics, 1);
                var body = BodyFactory.CreateRectangle(world, brick.Size.X * ViewToPhysics, brick.Size.Y * ViewToPhysics, 1);
                body.BodyType = BodyType.Dynamic;
                //fuck, it's body origin 
                body.Position = new Vector2(brick.Position.X + brick.Size.X / 2, brick.Position.Y + brick.Size.Y / 2) * ViewToPhysics;
                body.Restitution = 0.2f;
                body.Friction = 0.8f;
                body.UserData = brick;
                for (int i = 0; i < MAX_SIZE; i++)
                {
                    pieceArray[i].IgnoreCollisionWith(body);
                }
                bricks.Add(body);
            }
            //create bodies for birds
            foreach (var bird in level.Birds)
            {
                var body = BodyFactory.CreateCircle(world, BirdSize * 0.5f * ViewToPhysics, 1);
                body.BodyType = BodyType.Dynamic;
                body.Position = new Vector2(bird.X + BirdSize / 2, bird.Y + BirdSize / 2) * ViewToPhysics;
                body.Restitution = 0.2f;
                body.Friction = 0.8f;
                for (int i = 0; i < MAX_SIZE; i++)
                {
                    pieceArray[i].IgnoreCollisionWith(body);
                }
                birds.Add(body);
            }

            //create body for pig
            //pig = BodyFactory.CreateEllipse(world, (PigSize + 3) * 0.5f * ViewToPhysics, PigSize * 0.5f * ViewToPhysics, 360, 1);  //(world, PigSize * 0.5f * ViewToPhysics, 1);
            pig = BodyFactory.CreateCircle(world, PigSize * 0.5f * ViewToPhysics, 1);
            pig.BodyType = BodyType.Static;
            pig.Position = PivitPoint * ViewToPhysics;
            pig.Restitution = 0.2f;
            pig.Friction = 0.8f;
            for (int i = 0; i < MAX_SIZE; i++)
            {
                pieceArray[i].IgnoreCollisionWith(pig);
            }

            var ground = BodyFactory.CreateRectangle(world, 800 * ViewToPhysics, 20 * ViewToPhysics, 1);
            ground.BodyType = BodyType.Kinematic;
            ground.Friction = 0.8f;
            ground.Position = new Vector2(400, 480) * ViewToPhysics;
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            var mouseState = Mouse.GetState();

            if (mouseState.LeftButton == ButtonState.Pressed && pig.BodyType == BodyType.Static)
            {
                pig.Position = ClampPosition(new Vector2(mouseState.X, mouseState.Y)) * ViewToPhysics;
            }

            if (mouseState.LeftButton == ButtonState.Released && mouseStateLastFrame.LeftButton == ButtonState.Pressed &&
                pig.BodyType == BodyType.Static)
            {
                pig.Position = ClampPosition(new Vector2(mouseState.X, mouseState.Y)) * ViewToPhysics;
                pig.BodyType = BodyType.Dynamic;

                Vector2 force = PivitPoint - pig.Position * 50;
                pig.ApplyLinearImpulse(force * 0.1f);
            }

            if (mouseState.LeftButton == ButtonState.Released && pig.BodyType == BodyType.Dynamic && 
               (!pig.Awake || IsOutsideBounds(pig.Position * PhysicsToView)) || Keyboard.GetState().IsKeyDown(Keys.Space))
            {
                pig.Position = PivitPoint * ViewToPhysics;
                pig.BodyType = BodyType.Static;
            }
            mouseStateLastFrame = mouseState;

            world.Step(Math.Min((float)gameTime.ElapsedGameTime.TotalSeconds, 1.0f / 30));

            base.Update(gameTime);
        }

        private void PostSolveDelegate(Contact contact, ContactConstraint impulse)
        {
            float maxImpulse = 0;
            for (int i = 0; i < impulse.Manifold.PointCount; i++)
            {
                maxImpulse = Math.Max(maxImpulse, impulse.Manifold.Points[i].NormalImpulse);
            }

            if (maxImpulse > 2)
            {
                if (birds.Contains(impulse.BodyA))
                {
                    birds.Remove(impulse.BodyA);
                    world.RemoveBody(impulse.BodyA);
                }
                if (birds.Contains(impulse.BodyB))
                {
                    birds.Remove(impulse.BodyB);
                    world.RemoveBody(impulse.BodyB);
                }

                const int R = 10;
                const int maxForce = 8;
                if ((pig == impulse.BodyA)&&(bricks.Contains(impulse.BodyB)))
                {
                    Random r = new Random();
                    Random ar = new Random();
                    float x, y;
                    for (int i = 0; i < MAX_SIZE; i++)
                    {
                        float anguleRotation = ar.Next(-10, 10);
                        y = (float)r.Next(-R * 1000, R * 1000)/1000;
                        x = (float)r.Next((int)-Math.Sqrt(R*R-y*y)*1000, (int)Math.Sqrt(Math.Sqrt(R*R-y*y)*1000))/1000;
                        //Vector2 randomPosition = new Vector2(r.Next(-60, 60), r.Next(-60, 60)) * ViewToPhysics;
                        Vector2 randomForce = new Vector2(x, y);
                        randomForce.Normalize(); randomForce.X *= maxForce; randomForce.Y *= maxForce;
                        bricks.Remove(impulse.BodyB);
                        world.RemoveBody(impulse.BodyB);
                        pieceArray[i].Position = impulse.BodyB.Position ;
                        //pieceArray[i].Position = impulse.BodyB.Position + randomPosition;
                        pieceArray[i].Restitution = 0.2f;
                        pieceArray[i].Friction = 0.8f;
                        pieceArray[i].Rotation = anguleRotation;
                        pieceArray[i].AngularVelocity = 100f;
                        pieceArray[i].BodyType = BodyType.Dynamic;
                        OrientedBox ord1 = new OrientedBox();
                        ord1.Position = impulse.BodyB.Position;
                        ord1.Size = new Vector2(brickPiece1.Width/2, brickPiece1.Height/2);
                        pieceArray[i].UserData = ord1;
                        brickPiece.Add(pieceArray[i]);
                        pieceArray[i].ApplyForce(randomForce);
                    }
                }
                if ((pig == impulse.BodyB) && (bricks.Contains(impulse.BodyA)))
                {
                    Random r = new Random();
                    Random ar = new Random();
                    float x, y;
                    for (int i = 0; i < MAX_SIZE; i++)
                    {
                        float anguleRotation = ar.Next(-10, 10);
                        y = (float)r.Next(-R * 1000, R * 1000) / 1000;
                        x = (float)r.Next((int)-Math.Sqrt(R * R - y * y) * 1000, (int)Math.Sqrt(Math.Sqrt(R * R - y * y) * 1000))/1000;
                        //Vector2 randomPosition = new Vector2(r.Next(-60, 60), r.Next(-60, 60)) * ViewToPhysics ;                                              
                        Vector2 randomForce = new Vector2(x, y);
                        randomForce.Normalize(); randomForce.X *= maxForce; randomForce.Y *= maxForce;
                        bricks.Remove(impulse.BodyA);
                        world.RemoveBody(impulse.BodyA);
                        pieceArray[i].Position = impulse.BodyB.Position;
                        //pieceArray[i].Position = impulse.BodyB.Position + randomPosition;
                        pieceArray[i].Restitution = 0.2f;
                        pieceArray[i].Friction = 0.8f;
                        pieceArray[i].Rotation = anguleRotation;
                        pieceArray[i].AngularVelocity = 100f;
                        pieceArray[i].BodyType = BodyType.Dynamic;
                        OrientedBox ord1 = new OrientedBox();
                        //ord1.Position = impulse.BodyA.Position + randomPosition;
                        ord1.Position = impulse.BodyA.Position;
                        ord1.Size = new Vector2(brickPiece1.Width/2, brickPiece1.Height/2);
                        pieceArray[i].UserData = ord1;
                        brickPiece.Add(pieceArray[i]);
                        pieceArray[i].ApplyForce(randomForce);
                    }
                }
            }
        }
        private Vector2 ClampPosition(Vector2 position)
        {
            Vector2 direction = PivitPoint - position;
            float inv = MathHelper.Clamp(direction.Length(), 10, 60) / direction.Length();
            direction.X *= inv;
            direction.Y *= inv;
            return PivitPoint - direction;
        }

        private bool IsOutsideBounds(Vector2 position)
        {
            return !new Rectangle(-100, -100, 1000, 600).Contains(new Point((int)position.X, (int)position.Y));
        }
    
        
        protected override void Draw(GameTime gameTime)
        {
           
            GraphicsDevice.Clear(Color.CornflowerBlue);            
            spriteBatch.Begin();
            spriteBatch.Draw(background, GraphicsDevice.Viewport.Bounds, Color.White);
            foreach (var body in brickPiece)
            {               
                spriteBatch.Draw(brickPiece1, (body.Position) * PhysicsToView, null, Color.White, body.Rotation,
                    new Vector2(brickPiece1.Width * 0.5f, brickPiece1.Height * 0.5f),
                    new Vector2(((OrientedBox)body.UserData).Size.X / brickPiece1.Width,
                                ((OrientedBox)body.UserData).Size.Y / brickPiece1.Height), SpriteEffects.None, 0);                
            }
            
            
            foreach (var body in bricks)
            {
                spriteBatch.Draw(brickTexture, body.Position*PhysicsToView, null, Color.White, body.Rotation,
                    new Vector2(brickTexture.Width * 0.5f, brickTexture.Height * 0.5f),
                    new Vector2(((OrientedBox)body.UserData).Size.X / brickTexture.Width,
                                ((OrientedBox)body.UserData).Size.Y / brickTexture.Height), SpriteEffects.None, 0);
            }

            foreach (var body in birds)
            {
                spriteBatch.Draw(birdTexture, body.Position * PhysicsToView, null, Color.White, body.Rotation,
                    new Vector2(birdTexture.Width * 0.5f, birdTexture.Height * 0.5f),
                    new Vector2((BirdSize / birdTexture.Width),
                                (BirdSize / birdTexture.Height)), SpriteEffects.None, 0);
            }

            spriteBatch.Draw(pigTexture, pig.Position * PhysicsToView, null, Color.White, pig.Rotation,
                    new Vector2(pigTexture.Width * 0.5f, pigTexture.Height * 0.5f),
                    new Vector2((PigSize / pigTexture.Width),
                                (PigSize / pigTexture.Height)), SpriteEffects.None, 0);

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
//y(0,R)
//x(-math.sqrt(
